import mapboxgl from 'mapbox-gl';
import type { Mapless } from '..';
import IdHub, { type IdHubType } from '../core/IdHub';
import type { Handler } from '../core/Listener';
import { EmitManager } from '../core/manager';
import { getOption } from '../core/utils';
import type {
  ManagerOptions,
  Node,
  NodeManagerEvents,
  NodeManagerOptions,
} from '../index.d';

type ClusterIdHubType = IdHubType & {
  clusterLayerId: string;
  clusterLayerSelectId: string;
  clusterLayerCountId: string;
  clusterLayerPointId: string;
  clusterSourceId: string;
};

export default class ClusterManager extends EmitManager<
  NodeManagerEvents,
  ClusterIdHubType
> {
  private nodes: Node[] | undefined = undefined;

  private iconSize: number = 0.6;

  constructor(mapless: Mapless, options?: ManagerOptions) {
    super(mapless, options, 'cluster');
    this.onInitialized();
    // 设置是否显示文字，默认为false
    this.iconSize = getOption<number>('iconSize', options, 0.6);
    this.setupClickHandler();
    this.setupHoverHandler();
    this.nodes = [];
  }

  override createIdHub(_idHub?: IdHubType) {
    const newHub = new IdHub({
      layers: {
        clusterLayerId: 'dzmap-cluster-layer',
        clusterLayerSelectId: 'dzmap-cluster-select-layer',
        clusterLayerCountId: 'dzmap-cluster-count-layer',
        clusterLayerPointId: 'dzmap-cluster-point-layer',
      },
      sources: {
        clusterSourceId: 'dzmap-cluster-source',
      },
    }) as ClusterIdHubType;
    super.createIdHub(newHub);
  }

  override initLayers() {
    if (!this.mapless) return;
    const sid = this.idHub?.clusterSourceId;
    this.mapless.addEmptySource(sid, {
      cluster: true,
      clusterMaxZoom: 14,
      clusterRadius: 50,
    });
    this.mapless.addLayer({
      id: this.idHub?.clusterLayerId,
      type: 'circle',
      source: sid,
      filter: ['has', 'point_count'],
      paint: {
        'circle-color': [
          'step',
          ['get', 'point_count'],
          '#51bbd6',
          100,
          '#f1f075',
          750,
          '#f28cb1',
        ],
        'circle-radius': ['step', ['get', 'point_count'], 20, 100, 30, 750, 40],
      },
    });
    this.mapless.addLayer({
      id: this.idHub?.clusterLayerCountId,
      type: 'symbol',
      source: sid,
      filter: ['has', 'point_count'],
      layout: {
        'text-field': ['get', 'point_count_abbreviated'],
        'text-font': ['Open Sans Regular', 'Arial Unicode MS Regular'],
        'text-size': 12,
      },
    });
    this.mapless.addLayer({
      id: this.idHub?.clusterLayerSelectId,
      type: 'circle',
      source: sid,
      // filter: ['==', ['get', 'selected'], true],
      paint: {
        'circle-radius': 24, // 保持与普通图标相同大小
        'circle-color': [
          'case',
          ['==', ['get', 'selected'], true],
          'rgba(0, 120, 255, 0.3)',
          'rgba(0, 120, 255, 0.1)',
        ], // 半透明蓝色
        'circle-stroke-width': [
          'case',
          ['==', ['get', 'selected'], true],
          3,
          1,
        ], // 增加边框宽度
        'circle-stroke-color': '#1890ff', // 蓝色边框
      },
    });
    this.mapless.addLayer({
      id: this.idHub?.clusterLayerPointId,
      type: 'symbol',
      source: sid,
      filter: ['!', ['has', 'point_count']],
      layout: {
        'icon-image': ['case', ['has', 'icon'], ['get', 'icon'], 'deafult-6'],
        'icon-size': ['get', 'iconSize'],
        'text-field': ['get', 'name'],
        'text-font': ['Open Sans Regular', 'Arial Unicode MS Regular'],
        'text-offset': [0, 2.0],
        'text-anchor': 'top',
      },
      paint: {
        'text-color': this.mapless.isDark()
          ? 'rgba( 255, 255, 255,0.8)'
          : 'rgba(20, 20, 20, 0.8)',
        'text-halo-color': this.mapless.isDark()
          ? 'rgba( 20, 20, 20,0.8)'
          : 'rgba( 255, 255, 255,0.8)',
        'text-halo-width': 1, // 阴影宽度
        'text-halo-blur': 0, // 阴影模糊度
        // TODO experimental
        // "symbol-z-offset": ["get", "altitude"]
      },
    });
  }

  override onStyleChanged = (tag: string) => {
    if (this.idHub?.clusterLayerPointId) {
      this.mapless?.map?.setPaintProperty(
        this.idHub?.clusterLayerPointId,
        'text-color',
        this.mapless.isDark(tag)
          ? 'rgba( 255, 255, 255,0.8)'
          : 'rgba(20, 20, 20, 0.8)',
      );
      this.mapless?.map?.setPaintProperty(
        this.idHub?.clusterLayerPointId,
        'text-halo-color',
        this.mapless.isDark(tag)
          ? 'rgba(20, 20, 20, 0.8)'
          : 'rgba( 255, 255, 255,0.8)',
      );
    }
  };

  drawCluster(nodes: Node[]) {
    // 缓存当前stations
    this.nodes = nodes;
    // 创建GeoJSON数据
    const features = nodes.map((node) => ({
      type: 'Feature',
      properties: {
        id: node.id,
        icon: node.icon,
        name: node.name,
        altitude: node.altitude,
        iconSize: this.iconSize,
      },
      geometry: {
        type: 'Point',
        coordinates: [node.longitude, node.latitude],
      },
    }));
    const data = { type: 'FeatureCollection', features } as GeoJSON.GeoJSON;
    this.mapless?.setData(this.idHub?.clusterSourceId, data);
  }

  // TODO 单个或多个节点： addCluster updateCluster removeCluster

  override destroy = () => {
    super.destroy();
    this.nodes = undefined;
  };

  override fitBounds = () => {
    if (!this.map) return;
    const bounds = new mapboxgl.LngLatBounds();
    this.nodes?.forEach((node) => {
      bounds.extend([node.longitude, node.latitude]);
    });
    this.map.fitBounds(bounds, {
      padding: 50,
    });
  };

  setupClickHandler() {
    if (!this.map) return;
    const clickHandler = (e: mapboxgl.MapMouseEvent) => {
      if (!this.map) return;
      const feature = e.features?.[0] as GeoJSON.Feature<GeoJSON.Point>;
      if (feature) {
        const clusterId = feature.properties?.cluster_id;
        if (clusterId) {
          const clsid = this.idHub?.clusterSourceId;
          if (clsid) {
            const source = this.map.getSource(clsid) as mapboxgl.GeoJSONSource;
            source?.getClusterLeaves(clusterId, 200, 0, (err, features) => {
              if (err || !features) return;
              const nodes = features.map((f) => f.properties as Node);
              this.emit('clusterClick', nodes);
            });
            // TODO 测试[0,0],[0,0],[0,0]...导致的异常
            // source?.getClusterExpansionZoom(clusterId, (err, zoom) => {
            //   if (err || !zoom) return;
            //   const center = [
            //     feature.geometry.coordinates[0],
            //     feature.geometry.coordinates[1]
            //   ] as LngLatLike;
            //   this.map?.easeTo({ center, zoom });
            // });
          }
        }
      }
    };
    this._listener?.add(
      'click',
      this.idHub?.clusterLayerId,
      clickHandler as Handler,
    );
    const clickNodeHandler = (e: mapboxgl.MapMouseEvent) => {
      if (!this.map) return;
      const feature = e.features?.[0] as GeoJSON.Feature<GeoJSON.Point>;
      if (feature) {
        const curr = this.nodes?.find(
          (node) => node.id === feature.properties?.id,
        );
        curr && this.emit('nodeClick', curr);
      }
    };
    this._listener?.add(
      'click',
      this.idHub?.clusterLayerPointId,
      clickNodeHandler as Handler,
    );
  }

  setupHoverHandler() {
    const enterHandler = (e: mapboxgl.MapMouseEvent) => {
      const feature = e.features?.[0];
      if (feature) {
        this.mapless?.setCursor('pointer');
      }
    };
    this._listener?.add(
      'mouseenter',
      this.idHub?.clusterLayerSelectId,
      enterHandler as Handler,
    );
    const leaveHandler = () => {
      this.mapless?.setCursor('');
    };
    this._listener?.add(
      'mouseleave',
      this.idHub?.clusterLayerSelectId,
      leaveHandler as Handler,
    );
  }
}

export const attachCluster = (
  mapless: Mapless | undefined,
  name?: string,
  options?: NodeManagerOptions,
) => mapless?.use(ClusterManager, name, options);
export const getCluster = (mapless: Mapless | undefined, name?: string) =>
  mapless?.get(ClusterManager, name);
export const detachCluster = (mapless: Mapless | undefined, name?: string) =>
  mapless?.unuse(ClusterManager, name);
